1 module assert_that; 2 3 4 mixin template assertThat(string name, alias lhs, alias matcher) 5 { 6 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = () 7 { 8 import std.format : format; 9 10 mixin("auto %s = %s;".format(name, lhs)); 11 mixin assertThat!(name, matcher); 12 13 return 0; 14 }(); 15 } 16 17 mixin template assertThat(string lhs, alias matcher) 18 { 19 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = () 20 { 21 import std.conv : to; 22 import std.string : join; 23 24 mixin matcher.match!(lhs, matcher.args); 25 26 return 0; 27 }(); 28 } 29 30 31 template op(string operator, alias rhs, string file = __FILE__, ulong line = __LINE__) 32 { 33 import std.meta : AliasSeq; 34 alias args = AliasSeq!(operator, rhs, file, line); 35 36 mixin template match(string lhs, string operator, alias rhs, string file, ulong line) 37 { 38 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = delegate int() 39 { 40 import std.format : format; 41 42 static if (__traits(compiles, !mixin(lhs ~ operator ~ rhs.stringof))) 43 { 44 import std.conv : to; 45 46 mixin( 47 "#line %d \"%s\"\n".format(line, file) ~ 48 q{assert(mixin(lhs ~ operator ~ rhs.stringof), lhs ~ ": actual " ~ mixin(lhs).to!string ~ ": expected " ~ operator ~ " " ~ rhs.stringof);} 49 ); 50 51 return 0; 52 } 53 else 54 { 55 mixin( 56 "#line %d \"%s\"\n".format(line, file) ~ 57 q{assert(false, lhs ~ ": invalid expression: " ~ lhs ~ " " ~ operator ~ " " ~ rhs.stringof);} 58 ); 59 } 60 }(); 61 } 62 } 63 64 template eq(alias rhs, string file = __FILE__, ulong line = __LINE__) 65 { 66 alias eq = op!("==", rhs, file, line); 67 } 68 69 70 template array(string file = __FILE__, ulong line = __LINE__) 71 { 72 template _(matchers...) 73 { 74 import std.meta : AliasSeq; 75 alias args = AliasSeq!(file, line, matchers); 76 77 mixin template match(string lhs, string file, ulong line, matchers...) 78 { 79 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = () 80 { 81 import assert_that : eq; 82 83 mixin eq!0.match!(lhs ~ ".length", "==", matchers.length, file, line); 84 85 foreach (i, matcher; matchers) 86 { 87 import std.conv : to; 88 89 mixin matcher.match!(lhs ~ "[" ~ i.to!string ~ "]", matcher.args); 90 } 91 92 return 0; 93 }(); 94 } 95 } 96 } 97 98 99 template field(string fieldName, alias matcher, string file = __FILE__, ulong line = __LINE__) 100 { 101 import std.meta : AliasSeq; 102 alias args = AliasSeq!(fieldName, matcher, file, line); 103 104 mixin template match(string lhs, string fieldName, alias matcher, string file, ulong line) 105 { 106 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = () 107 { 108 import std.format : format; 109 110 static if (__traits(compiles, mixin(lhs ~ "." ~ fieldName))) 111 mixin matcher.match!(lhs ~ "." ~ fieldName, matcher.args); 112 else 113 mixin( 114 "#line %d \"%s\"\n".format(line, file) ~ 115 q{assert(false, lhs ~ "." ~ fieldName ~ ": field '" ~ fieldName ~ "' does not exist");} 116 ); 117 118 return 0; 119 }(); 120 } 121 } 122 123 124 template fields(string file = __FILE__, ulong line = __LINE__) 125 { 126 template _(matchers...) 127 { 128 import std.meta : AliasSeq; 129 alias args = AliasSeq!(file, line, matchers); 130 131 mixin template match(string lhs, string file, ulong line, matchers...) 132 { 133 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = () 134 { 135 import std.traits : FieldNameTuple; 136 import assert_that : eq; 137 138 mixin eq!0.match!("FieldNameTuple!(typeof(" ~ lhs ~ ")).length", "==", matchers.length, file, line); 139 140 foreach (i, field; FieldNameTuple!(typeof(mixin(lhs)))) 141 { 142 import std.traits : Alias; 143 import std.conv : to; 144 145 mixin Alias!(matchers[i]).match!(lhs ~ "." ~ field, matchers[i].args); 146 } 147 148 return 0; 149 }(); 150 } 151 } 152 } 153 154 155 template all(matchers...) 156 { 157 alias args = matchers; 158 159 mixin template match(string lhs, matchers...) 160 { 161 int VARIABLE_FOR_ASSERT_THAT_DONT_REFER_ME = () 162 { 163 foreach (matcher; matchers) 164 { 165 mixin matcher.match!(lhs, matcher.args); 166 } 167 168 return 0; 169 }(); 170 } 171 }